unit uSceneGame;

interface

uses uScene, Types, uScript, uSceneInfo;  

type
  TScrollDir = (sdNone, sdCenter, sdLeft, sdRight, sdTop, sdDown);
  
  TSceneGame = class(TScene)
  private
    M: TPoint;
    FL, FT: Word;
    Script: TScript;
    ScrollDir: TScrollDir; 
    SceneInfo: TSceneInfo;
    function CellCoord(X, Y: Integer): TPoint;
  public
    procedure Render; override;
    procedure KeyDown(var Key: Word); override;
    constructor Create;
    destructor Destroy; override;
    procedure Timer; override;
    procedure EditMap(X, Y, Z: Integer);
    procedure MouseMove(X, Y: Integer); override; 
    procedure MouseUp(Button: TMouseBtn); override;
    procedure MouseDn(Button: TMouseBtn); override;
    procedure Scroll(AX, AY: Byte; AScrollDir: TScrollDir);
    procedure RenderPanel;
    procedure Cursor(X, Y: Integer);
    function IsMapCreature(X, Y: Integer): ShortInt;
  end;

var
  SceneGame: TSceneGame;

implementation

uses Graphics, Windows, Forms, SysUtils, uGraph, uBox, uMap, uTile, uUtils,
  uSprites, uEnemy, uEdit, uHero, uMain, uScenes;

{ TSceneGame }

procedure TSceneGame.MouseUp(Button: TMouseBtn); 
var
  ID: ShortInt;
  X, Y, Z: Byte;
  Pt: TPoint;
begin
  Z := 0;
  // Minimap
  if PointInRect(M.X, M.Y, FL - 2, FT - 2,
    Map.Width + 3, Map.Height + 3) then
  begin
    Scroll(M.X - FL, M.Y - FT, sdCenter);
    Render;
    Exit;
  end;

  //
  if PointInRect(M.X, M.Y, 0, 0,
    ScreenWidth * TileSize, Graph.Surface.Height) then
  begin
    Pt := CellCoord(M.X, M.Y);
    X := Pt.X;
    Y := Pt.Y;
    if IsEdit then
    begin
      EditMap(X, Y, Z);
      Exit;
    end;

    //
    for Z := lrCrt downto lrObj do
    begin
      case Z of
        lrCrt:
        begin
          ID := IsMapCreature(X, Y);
          if (ID >= 0) then
          begin 
            Script.Run(X, Y);
            Box('Enemy ID: ' + IntToStr(ID));
            //Exit;
          end;
        end;
        lrObj:
        begin
          ID := Map.FMap[Y][X][Z];   
          if (ID >= 0) then 
          begin
            if (Button = mbRight) then  
            begin
              // Info
              Screen.Cursor := 1;
              SceneInfo.Back.Assign(Graph.Surface);
              SceneInfo.ObjID := ID;
              Scenes.Scene := SceneInfo;
            end else begin
              // Use
              Script.Run(X, Y);
              Box('Object ID: ' + IntToStr(ID));
            end;
            //Exit;
          end;
        end; 
        
      end;
    end;
        
    // Hero
    begin
      Hero.ProceedPath(Pt);
      Render;
    end;
    
  end;    
end;

procedure TSceneGame.MouseDn(Button: TMouseBtn);
begin
  inherited;

end;

function TSceneGame.CellCoord(X, Y: Integer): TPoint;
begin
  Result.X := MapLeft + (X div TileSize);
  Result.Y := MapTop + (Y div TileSize);
end;

constructor TSceneGame.Create;
begin
  ScrollDir := sdCenter;
  Script := TScript.Create;
  SceneInfo := TSceneInfo.Create;
end;

destructor TSceneGame.Destroy;     
begin
  SceneInfo.Free;
  Script.Free;
  inherited;
end;

procedure TSceneGame.KeyDown(var Key: Word);
begin
  case Key of
    ord('E'): fEdit.ShowModal;
    37: begin
          Scroll(0, 0, sdLeft);
          Render;
        end;  
    38: begin
          Scroll(0, 0, sdTop);
          Render;
        end;  
    39: begin
          Scroll(0, 0, sdRight);
          Render;
        end;  
    40: begin
          Scroll(0, 0, sdDown);
          Render;
        end;  
  end;
end;

procedure TSceneGame.MouseMove(X, Y: Integer);
begin
  Self.Cursor(X, Y);
  M.X := X;
  M.Y := Y;
end;

procedure TSceneGame.Render;
const
  ofs = 13 + TileSize;
var
  T: Cardinal;
  H: ShortInt;
  X, Y, Z, DX, DY: Integer;
begin
  with Graph.Surface.Canvas do
  begin
    FT := 24;
    FL := (ScreenWidth * TileSize) + FT;
    T := GetTickCount;
    begin    
      // Terrain
      for Y := 0 to ScreenHeight - 1 do
        for X := 0 to ScreenWidth - 1 do
          Draw(X * TileSize, Y * TileSize, 
            Tile[Map.FMap[MapTop + Y][MapLeft + X][lrTerrain]]);
            
      // Grid
      if IsEdit and IsGrid then
      begin
        Pen.Color := clYellow;
        for Y := 0 to ScreenHeight - 1 do
        begin
          MoveTo(0, Y * TileSize);
          LineTo(ScreenWidth * TileSize, Y * TileSize);
        end;      
        for X := 0 to ScreenWidth - 1 do
        begin
          MoveTo(X * TileSize, 0);
          LineTo(X * TileSize, ScreenHeight * TileSize);
        end;
      end;       

      // Buildings, Enemies
      for Z := lrObj to lrPath do
        for Y := 0 to ScreenHeight do
          for X := 0 to ScreenWidth do
          begin
            DX := (X - 1) * TileSize;
            DY := (Y - 1) * TileSize;
            if (Map.FMap[MapTop + Y][MapLeft + X][Z] >= 0) then
            case Z of
              lrObj:
                Graph.Surface.Canvas.Draw(DX, DY,
                  Sprite[spObj][Map.FMap[MapTop + Y][MapLeft + X][Z]]);
              lrCrt:
                Graph.Surface.Canvas.Draw(DX, DY,
                  Sprite[spCrt][Map.FMap[MapTop + Y][MapLeft + X][Z]]);
              lrPath:
                begin
                  Pen.Color := Random($FFFFFF);
                  Graph.Surface.Canvas.Ellipse(DX + ofs, DY + ofs, DX + ofs + 6, DY + ofs + 6);
                end;
            end;
          end;
               
      // Hero
      Graph.Surface.Canvas.Draw((Hero.Pos.X - MapLeft - 1) * TileSize, 
        (Hero.Pos.Y - MapTop - 1) * TileSize, Sprite[spCrt][8]);
      
      //
      if IsEdit and not IsMove then
      begin
        Brush.Style := bsBDiagonal;       
        for Z := lrObj to lrCrt do
          for Y := 0 to ScreenHeight - 1 do
            for X := 0 to ScreenWidth - 1 do
            begin
              DX := X * TileSize;
              DY := Y * TileSize;
              H := Map.FMap[MapTop + Y][MapLeft + X][Z];
              if (H >= lrStop) then 
              begin
                if (H = lrStop) then Pen.Color := clRed;
                if (H >= 0) then 
                begin
                  Pen.Color := clYellow;
                  if (Z = lrCrt) then 
                  begin
                    Rectangle(DX - 30, DY - 30, DX + 63, DY + 63);
                    Continue;
                  end;  
                end;
                Rectangle(DX + 3, DY + 3, DX + 30, DY + 30);
              end;
            end;   
      end;
      Brush.Style := bsClear;    
      
{      // Render creatures      
      with Creatures do if (Length(Enemy) > 0) then
        for I := 0 to Length(Enemy) - 1 do 
        begin
          if PointInRect(Enemy[I].X, Enemy[I].Y, MapLeft - 1, MapTop - 1, 
            MapLeft + ScreenWidth, MapTop + ScreenHeight) then
              Graph.Surface.Canvas.Draw(((Enemy[I].X - 1) - MapLeft) * TileSize, 
                ((Enemy[I].Y - 1) - MapTop) * TileSize, Creature[Enemy[I].ID]); 
        end;       }
            
    end;        
    RenderPanel;             
    TextOut(32, 32, IntToStr(GetTickCount - T));         
  end;
  Graph.Render;
end;  

procedure TSceneGame.RenderPanel;
var
  Pt: TPoint;
begin
  with Graph.Surface.Canvas do
  begin
    Brush.Color := 0;
    FillRect(Rect(FL - 24, 0, Graph.Surface.Width, Graph.Surface.Height));
    Draw(FL, FT, Map.Minimap);
    Pen.Color := clYellow;
    Brush.Style := bsClear;
    Rectangle(FL - 1, FT - 1, FL + Map.Width, FT + Map.Height);
    Rectangle(FL + MapLeft - 1, FT + MapTop - 1,
      FL + MapLeft + ScreenWidth + 1,
        FT + MapTop + ScreenHeight + 1);
    Font.Color := clWhite;
    Pt := CellCoord(M.X, M.Y);
    TextOut(1100, 300, Format('%d %d', [Pt.X, Pt.Y]));
  end;  
end;

procedure TSceneGame.Timer;
begin
  Hero.Update;
  Render;
  if (M.X < ScrollPadding) and (M.Y < ScrollPadding) then 
  begin
    Scroll(0, 0, sdLeft);
    Scroll(0, 0, sdTop);
    Render;
    Exit;
  end;
  if (M.X < ScrollPadding) 
    and (M.Y > (Graph.Surface.Height - ScrollPadding)) then 
  begin
    Scroll(0, 0, sdLeft);
    Scroll(0, 0, sdDown);
    Render;
    Exit;
  end;
  if (M.X > (Graph.Surface.Width - ScrollPadding)) 
    and (M.Y < ScrollPadding) then 
  begin
    Scroll(0, 0, sdRight);
    Scroll(0, 0, sdTop);
    Render;
    Exit;
  end;
  if (M.X > (Graph.Surface.Width - ScrollPadding)) 
    and (M.Y > (Graph.Surface.Height - ScrollPadding)) then 
  begin
    Scroll(0, 0, sdRight);
    Scroll(0, 0, sdDown);
    Render;
    Exit;
  end;

  if (M.X < ScrollPadding) then 
  begin
    Scroll(0, 0, sdLeft);
    Render;
    Exit;
  end;
  if (M.Y < ScrollPadding) then 
  begin
    Scroll(0, 0, sdTop);
    Render;
    Exit;
  end;
  if (M.X > (Graph.Surface.Width - ScrollPadding)) then 
  begin
    Scroll(0, 0, sdRight);
    Render;
    Exit;
  end;
  if (M.Y > (Graph.Surface.Height - ScrollPadding)) then 
  begin
    Scroll(0, 0, sdDown);
    Render;
    Exit;
  end;
end;

procedure TSceneGame.Scroll(AX, AY: Byte; AScrollDir: TScrollDir);
var
  I: Integer;
begin
  case AScrollDir of
    sdCenter:
    begin
      MapLeft := AX - (ScreenWidth div 2);
      MapTop := AY - (ScreenHeight div 2);

      I := Map.Height - ScreenHeight - 1; 
      if (MapTop > I) then MapTop := I;
      if (MapTop < 0) then MapTop := 0;  

      I := Map.Width - ScreenWidth - 1;
      if (MapLeft > I) then MapLeft := I;
      if (MapLeft < 0) then MapLeft := 0;
    end;
    sdLeft:
    begin
      Dec(MapLeft);
      if (MapLeft < 0) then MapLeft := 0;
    end;
    sdRight: 
    begin
      Inc(MapLeft);
      I := Map.Width - ScreenWidth - 1;
      if (MapLeft > I) then MapLeft := I;
    end;
    sdTop: 
    begin
      Dec(MapTop);
      if (MapTop < 0) then MapTop := 0;
    end;
    sdDown:
    begin
      Inc(MapTop);
      I := Map.Height - ScreenHeight - 1;
      if (MapTop > I) then MapTop := I;
    end;
  end;
  ScrollDir := AScrollDir;
end;

procedure TSceneGame.EditMap(X, Y, Z: Integer);

  procedure Clear;
  begin
    Map.FMap[Y][X][lrObj]  := lrNone;
    Map.FMap[Y][X][lrCrt] := lrNone;
  end;
  
begin
  case fEdit.PageControl1.TabIndex of
    0:
    begin
      if fEdit.sbNone.Down then Exit;
        if fEdit.sbWater.Down then Map.FMap[Y][X][Z] := -1;
        if fEdit.sbGround.Down then Map.FMap[Y][X][Z] := Map.GetGroundSet;
        Map.MakeMap;
        Map.Terrain;
        Map.MakeMiniMap;  
        Render;
        Exit;
    end;
      1:
      begin
        // Delete
        if (MapObjectID = lrNone) and (MapEnemyID = lrNone)
          and (Map.FMap[Y][X][lrTerrain] in GroundSet) then
        begin
          Clear;
          Render;
          Exit;
        end;  
        // Add Object
        if (Map.FMap[Y][X][lrObj] = lrNone) 
          and (Map.FMap[Y][X][lrCrt] = lrNone)
          and (Map.FMap[Y][X][lrTerrain] in GroundSet) then
        begin  
          Clear;
          case MapSellectID of
            lrObj:  Map.FMap[Y][X][lrObj]  := MapObjectID;
            lrCrt: Map.FMap[Y][X][lrCrt] := MapEnemyID;
          end;
          Render;
          Exit;
        end;
      end;     
    end;
end;

function TSceneGame.IsMapCreature(X, Y: Integer): ShortInt;
var
  I, J: Word;
begin
  Result := lrNone;
  for I := Y - AggrZone to Y + AggrZone do
    for J := X - AggrZone to X + AggrZone do
      if Map.CellInMap(J , I) and (Map.FMap[I][J][lrCrt] >= 0) then
      begin
        Result := Map.FMap[I][J][lrCrt];
        Exit;
      end;
end;

procedure TSceneGame.Cursor(X, Y: Integer);
var
  Pt: TPoint;
begin
  if PointInRect(X, Y, 0, 0,
    ScreenWidth * TileSize, Graph.Surface.Height) then
  begin
    Pt := CellCoord(X, Y);
    if (IsMapCreature(Pt.X, Pt.Y) >= 0) then
      Screen.Cursor := 2
      else if (Map.FMap[Pt.Y][Pt.X][lrObj] >= 0) then
        Screen.Cursor := 3 else Screen.Cursor := 1;
  end else Screen.Cursor := 1;  
end;       

initialization
  SceneGame := TSceneGame.Create;

finalization
  SceneGame.Free;

end.
